﻿using System;
using System.CodeDom.Compiler;
using kp.compiler.contract;
using Microsoft.CSharp;

namespace kp.compiler
{
    
    public class Compiler: ICompiler
    {
        public Compiler()
        {
            
        }

        public Func<double, double> Compile(string formel)
        {
            string sourceCode = BuildFunctionSourceCode(formel);
            CompilerResults compiledSourceCode = CompileFunctionSource(sourceCode);
            return BuildFunction(compiledSourceCode);
        
        }

        public string BuildFunctionSourceCode(string formel) 
        {
            return
            @"
                            using System;
              
                            public static class Calculate
                            {
                                public static double Calc(double x)
                                {
                                    return $placeHolder;
                                }

                                public static double sin(double x)
                                {
                                    return Math.Sin(x);
                                }

                                public static double cos(double x)
                                {
                                    return Math.Cos(x);
                                }
                            }".Replace("$placeHolder", formel);
        }

        public CompilerResults CompileFunctionSource(string sourceCode)
        {
            CompilerParameters cp = new CompilerParameters();
            cp.GenerateExecutable = false;
            cp.GenerateInMemory = true;

            cp.ReferencedAssemblies.Add("System.dll");
            CSharpCodeProvider csc = new CSharpCodeProvider();
            
            return csc.CompileAssemblyFromSource(cp, sourceCode);
        }

        public Func<double, double> BuildFunction(CompilerResults compiledSource)
        {
            var calculatorClass = compiledSource.CompiledAssembly.GetType("Calculate");
            var calcMethod = calculatorClass.GetMethod("Calc");
            return (Func<double, double>) Delegate
                .CreateDelegate(typeof (Func<double, double>), calcMethod);
        }
    }
}
